package com.kevin.spring.reference;

import com.kevin.annotation.RemoteReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 每个类加载完成，实例化之前，判断该对象中是否又被remoteReference注解标注的属性
 * 如果有，将用来创建代理的工厂bean加载到spring容器
 */
@Slf4j
public class SpringRpcBeanPostProcessor implements BeanFactoryPostProcessor, BeanClassLoaderAware {

    private ClassLoader classLoader;

    private RpcClientProperties rpcClientProperties;

    //保存发布的引用bean的信息
    private final Map<String, BeanDefinition> rpcRefBeanDefinition=new ConcurrentHashMap<>();

    public SpringRpcBeanPostProcessor(RpcClientProperties rpcClientProperties) {
        this.rpcClientProperties = rpcClientProperties;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        //从所有加载完成的bean中找到被RemoteReference标注的属性，生成对应的beanDefinition放入rpcRefBeanDefinition
        for (String beanDefinitionName : configurableListableBeanFactory.getBeanDefinitionNames()) {
            BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanDefinitionName);
            if(null!=beanDefinition.getBeanClassName()){
                Class<?> beanClazz = ClassUtils.resolveClassName(beanDefinition.getBeanClassName(), classLoader);

                ReflectionUtils.doWithFields(beanClazz, this::parseAnnotation);
            }

        }
        //注册bean
        BeanDefinitionRegistry registry=(BeanDefinitionRegistry)configurableListableBeanFactory;
        rpcRefBeanDefinition.forEach((beanName,beanDefinifition)->{
            registry.registerBeanDefinition(beanName, beanDefinifition);
            log.info("registered RpcReferenceBean {} success",beanName);
        });
    }


    private void parseAnnotation(Field field) {
        RemoteReference remoteReference = AnnotationUtils.getAnnotation(field, RemoteReference.class);
        if(null!=remoteReference){
            BeanDefinition beanDefinition= BeanDefinitionBuilder.genericBeanDefinition(SpringRpcReferenceBean.class)
                    .setInitMethodName("init")
                    .addPropertyValue("interfaceClass", field.getType())
                    .addPropertyValue("serverIp",rpcClientProperties.getServerIp())
                    .addPropertyValue("serverPort",rpcClientProperties.getServerPort())
                    .getBeanDefinition();

            rpcRefBeanDefinition.put(field.getName(),beanDefinition);
        }
    }

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }
}
